﻿#include "detectimage.h"
#include <stdlib.h>




extern QQueue<cv::Mat> videoFrameQueue;
extern QMutex videoMutex;

QQueue<ROI_FRAME> roiFrameQueue;
QMutex detectMutex;

DetectImage::DetectImage()
{
}

int DetectImage::pythonInit(const char *module_name, const char *class_name)
{
    // 设置 python 环境目录
    Py_SetPythonHome(reinterpret_cast<const wchar_t*>(L"G:\\RJAZ\\Miniconnda\\data"));
    Py_Initialize(); // 初始化
    if(!Py_IsInitialized())   return ERROR_PyInit;

    PyRun_SimpleString("import sys");                             // 加载 sys 模块
    PyRun_SimpleString("sys.path.append('/')");                   // 设置 python 文件搜索路径

    PyObject *pModule = PyImport_ImportModule(module_name);       // 调用的文件名
    Q_ASSERT(pModule != NULL);
    PyObject *pDict = PyModule_GetDict(pModule);                  // 加载文件中的函数名、类名
    freePyObject(pModule);
    Q_ASSERT(pDict != NULL);
    PyObject *pClass = PyDict_GetItemString(pDict, class_name);   // 获取类名
    freePyObject(pDict);
    Q_ASSERT(pClass != NULL);
    pDetect = PyObject_CallObject(pClass, nullptr);               // 实例化
    freePyObject(pClass);
    Q_ASSERT(pDetect != NULL);

    if (_import_array() < 0)  return ERROR_NumpyInit;                          // 加载 numpy 模块

    return ERROR_NO;
}

void DetectImage::pythonDeinit()
{
    _Py_XDECREF(pDetect);
    Py_Finalize();
}

void DetectImage::freePyObject(PyObject *obj)
{
    for (int i = 0; i < obj->ob_refcnt; i++)
        _Py_XDECREF(obj);
}



void DetectImage::loadAlgorithmModel()
{
    PyObject *ret = PyObject_CallMethod(pDetect, "load_model", ""); // 加载 YoloV 模型，最耗时的过程
    if(!ret)
    {
        emit sigSendErrorCode(ERROR_LoadModel);
    }
}

void DetectImage::setPause(bool flag)
{
    QMutexLocker locker(&mutex);
    IS_Pause = flag;
}

void DetectImage::setRun(bool flag)
{
    QMutexLocker locker(&mutex);
    AlwaysRun = flag;
}

int DetectImage::detectImage(const char *fun, Mat srcImg, Mat &dstImg)
{
    PyObject *pFun = PyObject_GetAttrString(pDetect, fun); // 获取函数名
    Q_ASSERT(pFun != NULL);

    // 将 Mat 类型 转 PyObject* 类型
    PyObject *argList = PyTuple_New(1);
    npy_intp Dims[3] = {srcImg.rows, srcImg.cols, srcImg.channels()};
    int dim = srcImg.channels() == 1 ? 1 : 3;
    PyObject *PyArray = PyArray_SimpleNewFromData(dim, Dims, NPY_UBYTE, srcImg.data);
    PyTuple_SetItem(argList, 0, PyArray);

    // 带传参的函数执行
    PyObject *pRet = PyObject_CallObject(pFun, argList);
    _Py_XDECREF(argList); // 释放内存空间
    _Py_XDECREF(pFun); // 释放内存空间
//    Q_ASSERT(pRet != NULL);

    if(!PyTuple_Check(pRet))  return -1;// 检查返回值是否是元组类型， python返回值为 (mat,int)

    PyArrayObject *ret_array;

    PyObject *_ok = nullptr;
    PyObject *_x1 = nullptr;
    PyObject *_y1 = nullptr;
    PyObject *_x2 = nullptr;
    PyObject *_y2 = nullptr;

    PyArg_UnpackTuple(pRet, "ref", 6, 6, &ret_array ,&_ok, &_x1, &_y1, &_x2, &_y2); // 解析返回值
//    PyArg_Parse(_ok, "i", &isExist);
//    PyArg_Parse(_x1, "i", &x1);
//    PyArg_Parse(_y1, "i", &y1);
//    PyArg_Parse(_x2, "i", &x2);
//    PyArg_Parse(_y2, "i", &y2);

    // 转 Mat 类型
    dstImg = Mat(ret_array->dimensions[0], ret_array->dimensions[1], CV_8UC3, PyArray_DATA(ret_array)).clone();
//    Py_XDECREF(PyArray); // PyArray_SimpleNewFromData 为增加对PyArray的引用
    _Py_XDECREF(pRet); //  PyObject_CallObject
    _Py_XDECREF(pRet); // PyArg_UnpackTuple
    return 0;
}

int DetectImage::detectImageEx(const char *fun, Mat srcImg, Mat &dstImg, Mat &roiImg)
{
    PyObject *pFun = PyObject_GetAttrString(pDetect, fun); // 获取函数名
    Q_ASSERT(pFun != NULL);

    // 将 Mat 类型 转 PyObject* 类型
    PyObject *argList = PyTuple_New(1);
    npy_intp Dims[3] = {srcImg.rows, srcImg.cols, srcImg.channels()};
    int dim = srcImg.channels() == 1 ? 1 : 3;
    PyObject *PyArray = PyArray_SimpleNewFromData(dim, Dims, NPY_UBYTE, srcImg.data);
    PyTuple_SetItem(argList, 0, PyArray);

    // 带传参的函数执行
    PyObject *pRet = PyObject_CallObject(pFun, argList);
    _Py_XDECREF(argList); // 释放内存空间
    _Py_XDECREF(pFun); // 释放内存空间
//    Q_ASSERT(pRet != NULL);

    if(!PyTuple_Check(pRet))  return -1;// 检查返回值是否是元组类型， python返回值为 (mat,int)

    PyArrayObject *ret_array;

    PyObject *_ok = nullptr;
    PyObject *_x1 = nullptr;
    PyObject *_y1 = nullptr;
    PyObject *_x2 = nullptr;
    PyObject *_y2 = nullptr;

    PyArg_UnpackTuple(pRet, "ref", 6, 6, &ret_array ,&_ok, &_x1, &_y1, &_x2, &_y2); // 解析返回值
    x1 = 0;
    x2 = 0;
    y1 = 0;
    y2 = 0;
    isExist = 0;
    PyArg_Parse(_ok, "i", &isExist);
    PyArg_Parse(_x1, "i", &x1);
    PyArg_Parse(_y1, "i", &y1);
    PyArg_Parse(_x2, "i", &x2);
    PyArg_Parse(_y2, "i", &y2);

    // 转 Mat 类型
    dstImg = Mat(ret_array->dimensions[0], ret_array->dimensions[1], CV_8UC3, PyArray_DATA(ret_array)).clone();
    if(isExist == 1)
    {
        Point p1(x1,y1);
        Point p2(x2,y2);
        roiImg = dstImg(Rect(p1,p2)).clone();
    }

//    Py_XDECREF(PyArray); // PyArray_SimpleNewFromData 为增加对PyArray的引用
    _Py_XDECREF(pRet); //  PyObject_CallObject
    _Py_XDECREF(pRet); // PyArg_UnpackTuple
    return 0;
}

int DetectImage::detectImageEx2(const char *fun, Mat srcImg, ROI_FRAME &roiFrame)
{
    PyObject *pFun = PyObject_GetAttrString(pDetect, fun); // 获取函数名
    Q_ASSERT(pFun != NULL);

    // 将 Mat 类型 转 PyObject* 类型
    PyObject *argList = PyTuple_New(1);
    npy_intp Dims[3] = {srcImg.rows, srcImg.cols, srcImg.channels()};
    int dim = srcImg.channels() == 1 ? 1 : 3;
    PyObject *PyArray = PyArray_SimpleNewFromData(dim, Dims, NPY_UBYTE, srcImg.data);
    PyTuple_SetItem(argList, 0, PyArray);

    // 带传参的函数执行
    PyObject *pRet = PyObject_CallObject(pFun, argList);
    _Py_XDECREF(argList); // 释放内存空间
    _Py_XDECREF(pFun);    // 释放内存空间
//    Q_ASSERT(pRet != NULL);

    if(!PyTuple_Check(pRet))  return -1;// 检查返回值是否是元组类型， python返回值为 (mat,int)

    PyArrayObject *ret_array;

    PyObject *_ok = nullptr;
    PyObject *_x1 = nullptr;
    PyObject *_y1 = nullptr;
    PyObject *_x2 = nullptr;
    PyObject *_y2 = nullptr;

    PyArg_UnpackTuple(pRet, "ref", 6, 6, &ret_array ,&_ok, &_x1, &_y1, &_x2, &_y2); // 解析返回值
    unsigned int exist_=0,x1_=0,x2_=0,y1_=0,y2_=0;
    PyArg_Parse(_ok, "i", &exist_);
    PyArg_Parse(_x1, "i", &x1_);
    PyArg_Parse(_y1, "i", &y1_);
    PyArg_Parse(_x2, "i", &x2_);
    PyArg_Parse(_y2, "i", &y2_);

    // 转 Mat 类型
    Mat frame = Mat(ret_array->dimensions[0], ret_array->dimensions[1], CV_8UC3, PyArray_DATA(ret_array)).clone();

    roiFrame.x1 = x1_;
    roiFrame.x2 = x2_;
    roiFrame.y1 = y1_;
    roiFrame.y2 = y2_;
    roiFrame.exist = exist_;
    roiFrame.frame = frame;

    _Py_XDECREF(pRet); //  PyObject_CallObject
    _Py_XDECREF(pRet); // PyArg_UnpackTuple
    return 0;
}


void DetectImage::run()
{
    int ret = pythonInit("yolov5.temp", "YoloV5");
    if(ret != ERROR_NO)
    {
        emit sigSendErrorCode(ret);
        return;
    }
    loadAlgorithmModel();   /* 必须要在同一线程内 */

    while(AlwaysRun)
    {
        if(IS_Pause)
        {
            videoMutex.lock();
            bool isok = videoFrameQueue.isEmpty();
            videoMutex.unlock();

            if(!isok)
            {
                videoMutex.lock();
                Mat srcFrame = videoFrameQueue.dequeue();
                videoMutex.unlock();
                ROI_FRAME dstFrame;
                detectImageEx2("detect", srcFrame, dstFrame);

                detectMutex.lock();
                roiFrameQueue.enqueue(dstFrame);
                detectMutex.unlock();
            }
        }
        msleep(5);
    }
//    qDebug() << "stop";
//    pythonDeinit();
}
